package com.example.origin.redis.controller;

import com.example.origin.redis.utils.ProtostuffUtil;
import lombok.Data;
import lombok.SneakyThrows;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * @author yutao.zhang
 * @create 2023-07-20 17:03
 **/
@RestController
public class DemoController {


    @GetMapping("test")
    public void test() {
        User user = new User();
        user.setId(1);
        user.setAge(11);
        user.setName("jack");
        user.setDesc("i am king");
        //使用ProtostuffUtils序列化
        byte[] data = ProtostuffUtil.serialize(user);
        System.out.println("序列化后：" + Arrays.toString(data));
        User result = ProtostuffUtil.deserialize(data, User.class);
        System.out.println("反序列化后：" + result.toString());
    }

    @Cacheable(value = "user",keyGenerator = "wiselyKeyGenerator")
    @GetMapping("getUser")
    public User getUser(Integer id, String name) {
        User user = new User();
        user.setId(id);
        user.setAge(11);
        user.setName(name);
        user.setDesc("i am king");
        return user;
    }

    @CacheEvict(value = "user",allEntries = true)
    @GetMapping("delUser")
    public void delUser(Integer id) {
        redisTemplate.delete("user::"+id);
    }

    @Data
    public static class User {
        private Integer id;

        private String name;

        private Integer age;

        private String desc;
    }


    @Resource(name = "defaultRedisTemplate")
    private RedisTemplate<String,User> redisTemplate;

    @GetMapping("getUser2")
    public User getUser2(Integer id) {
        User user = new User();
        user.setId(id);
        user.setAge(11);
        user.setName("king");
        user.setDesc("i am king");
        redisTemplate.opsForValue().set("user::"+id,user);
        user = redisTemplate.opsForValue().get("user::" + id);
        return user;
    }


    @Resource(name = "protoStuffRedisTemplate")
    private RedisTemplate<String,User> protoStuffRedisTemplate;

    @GetMapping("getUser3")
    public User getUser3(Integer id) {
        User user = new User();
        user.setId(id);
        user.setAge(11);
        user.setName("king");
        user.setDesc("i am king");
        protoStuffRedisTemplate.opsForValue().set("user::"+id,user);
        user = protoStuffRedisTemplate.opsForValue().get("user::" + id);
        return user;
    }

    static List<User> users = new ArrayList<>();
    static {
        for (int i = 0; i < 1000000; i++) {
            User user = new User();
            user.setId(i);
            user.setAge(11);
            user.setName("king");
            user.setDesc("i am king");
            users.add(user);
        }
    }

    @Resource(name = "protoStuffRedisTemplate")
    private RedisTemplate<String,List<User>> usersProtoStuffRedisTemplate;

    @Resource(name = "defaultRedisTemplate")
    private RedisTemplate<String,List<User>> usersRedisTemplate;

    @Resource(name = "stringRedisTemplate")
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("getUserList")
    public void getUserList() {
        usersProtoStuffRedisTemplate.opsForValue().set("user::-1",users);
        List<User> userList = usersProtoStuffRedisTemplate.opsForValue().get("user::-1" );
        System.out.println(userList.size());
        System.out.println(userList.get(userList.size()-1));

        usersRedisTemplate.opsForValue().set("user::-2",users);
        userList = usersRedisTemplate.opsForValue().get("user::-2" );
        System.out.println(userList.size());
        System.out.println(userList.get(userList.size()-1));


        String s1 = stringRedisTemplate.opsForValue().get("user::-1");
        System.out.println(s1.length());
        String s2 = stringRedisTemplate.opsForValue().get("user::-2");
        System.out.println(s2.length());


    }

    @Resource
    Redisson redisson;

    @SneakyThrows
    @GetMapping("testRedisson")
    public void testRedisson(){
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+":start");
            lock(e->{
                try {
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println(Thread.currentThread().getName()+":stop");
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            });
        },"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+":start");
            lock(e->{
                try {
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println(Thread.currentThread().getName()+":stop");
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            });
        },"B").start();

        TimeUnit.SECONDS.sleep(20);

    }

    @SneakyThrows

    private void lock(Consumer consumer) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        RLock lock = redisson.getLock("lock");
        try {
//            if (lock.tryLock(3,5, TimeUnit.SECONDS)) {
//                consumer.accept(null);
//                if (lock.isHeldByCurrentThread()) {
//                    lock.unlock();
//                }
//            }
            lock.lockInterruptibly(3,TimeUnit.SECONDS);
            consumer.accept(null);
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        } finally {
            stopWatch.stop();
            System.out.println(Thread.currentThread().getName()+":"+stopWatch.getTotalTimeMillis());
        }
    }


}
